package com.xiyuan.smartutils.crypto;

import com.xiyuan.smartutils.enums.LetterCase;

import java.nio.charset.Charset;

/**
 * 十六进制加解密算法
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */
@SuppressWarnings({"all"})
public class HEX {
    
    private static final Charset UTF_8_C = Charset.forName("UTF-8");
    private static final char[] CSL = "0123456789abcdef".toCharArray();
    private static final char[] CSU = "0123456789ABCDEF".toCharArray();
    private static final String EMPTY = "";
    
    /**********************************************************************/
    /***************************  byte[] 转 Hex String  *******************/
    /**********************************************************************/
    
    /**
     * 将字节数组转换成十六进制字符串
     *
     * @param b 字节数组
     * @return 十六进制字符串
     */
    public static String toString(int b) {
        return Integer.toHexString(b);
    }
    
    /**
     * 将字节数组转换成十六进制字符串
     *
     * @param bytes    字节数组
     * @param spe      分隔符
     * @param caseType 字母大写
     * @return 十六进制字符串
     */
    public static String toString(String spe, LetterCase caseType, byte... bytes) {
        int spes = spe != null ? spe.length() : 0;
        char[] cs = caseType == LetterCase.UPPER ? CSU : CSL;
        StringBuilder strb = new StringBuilder(bytes.length * 2 + spes * (bytes.length - 1));
        for (int i = 0; i < bytes.length; i++) {
            if (i != 0 && spes > 0) {
                strb.append(spes);
            }
            
            strb.append(cs[(bytes[i] >> 4) & 0x0f]);
            strb.append(cs[bytes[i] & 0x0f]);
        }
        
        return strb.toString();
    }
    
    /**
     * 将字节数组转换成十六进制字符串
     *
     * @param payload 字节数组
     * @param spe     分隔符
     * @return 十六进制字符串
     */
    public static String toString(String spe, byte... payload) {
        return toString(spe, LetterCase.LOWER, payload);
    }
    
    /**
     * 将字节数组转换成十六进制字符串
     *
     * @param payload  字节数组
     * @param caseType 字母大写
     * @return 十六进制字符串
     */
    public static String toString(LetterCase caseType, byte... payload) {
        return toString(EMPTY, caseType, payload);
    }
    
    /**
     * 将字节数组转换成十六进制字符串
     *
     * @param payload 字节数组
     * @return 十六进制字符串
     */
    public static String toString(byte... payload) {
        return toString(EMPTY, LetterCase.LOWER, payload);
    }
    
    /**********************************************************************/
    /*************************** String 转 Hex ****************************/
    /**********************************************************************/
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload  字符串
     * @param charset  字符编码
     * @param sep      分隔符
     * @param caseType 字母大写
     * @return 十六进制字符串
     */
    public static String toString(String payload, Charset charset, String sep, LetterCase caseType) {
        byte[] bytes = charset == null ? payload.getBytes() : payload.getBytes(charset);
        return toString(sep, caseType, bytes);
    }
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload  字符串
     * @param charset  字符编码
     * @param sep      分隔符
     * @param caseType 字母大写
     * @return 十六进制字符串
     */
    public static String toString(String payload, String charset, String sep, LetterCase caseType) {
        return toString(payload, Charset.forName(charset), sep, caseType);
    }
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload 字符串
     * @param sep     分隔符
     * @return 十六进制字符串
     */
    public static String toString(String payload, String sep) {
        return toString(sep, LetterCase.LOWER, payload.getBytes());
    }
    
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload  字符串
     * @param caseType 字符串大写
     * @return 十六进制字符串
     */
    public static String toString(String payload, LetterCase caseType) {
        return toString(EMPTY, caseType, payload.getBytes());
    }
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload 字符串
     * @param charset 字符编码
     * @return 十六进制字符串
     */
    public static String toString(String payload, Charset charset) {
        return toString(EMPTY, LetterCase.LOWER, payload.getBytes(charset));
    }
    
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload 字符串
     * @return 十六进制字符串
     */
    public static String toString(String payload) {
        return toString(EMPTY, LetterCase.LOWER, payload.getBytes());
    }
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload 字符串
     * @param sep     分隔符
     * @return 十六进制字符串
     */
    public static String toStringUTF8(String payload, String sep) {
        return toString(sep, LetterCase.LOWER, payload.getBytes(UTF_8_C));
    }
    
    /**
     * 将字符串转换成十六进制字符串
     *
     * @param payload 字符串
     * @return 十六进制字符串
     */
    public static String toStringUTF8(String payload) {
        return toStringUTF8(payload, EMPTY);
    }
    
    
    /**********************************************************************/
    /***************************  Hex String 转 byte[]  *******************/
    /**********************************************************************/
    
    private static int getBytesLen(String payload, int sepLen, String sep, int prefixLen, String prefix, int unitLen,
                                   int radix, int suffixLen, String suffix) {
        int len = payload.length();
        int dataLen = 0;
        for (int i = 0; i < len; ) {
            
            // 分隔符
            if (i != 0 && sepLen > 0) {
                
                if (payload.indexOf(sep, i) != i) {
                    char ch = payload.charAt(i);
                    String msg = "Illegal hexadecimal string: missing separator \"" + sep + "\" before character '" + ch + "'. at index " + i;
                    throw new IllegalArgumentException(msg);
                }
                i += sepLen;
            }
            if (i >= len) {break;}
            // 前缀
            if (prefixLen > 0) {
                if (payload.indexOf(prefix, i) != i) {
                    char ch = payload.charAt(i);
                    String msg = "Illegal hexadecimal string: missing separator \"" + sep + "\" before character '" + ch + "'. at index " + i;
                    throw new IllegalArgumentException(msg);
                }
                i += prefixLen;
            }
            
            // 读取单位长度
            for (int j = 0; j < unitLen; j++) {
                char ch = payload.charAt(i++);
                if (Character.digit(ch, radix) < 0) {
                    throw new IllegalArgumentException("Illegal hexadecimal character `" + ch + "` at index " + i);
                }
            }
            dataLen++;
            
            // 后缀
            if (suffixLen > 0) {
                if (payload.indexOf(suffix, i) != i) {
                    throw new IllegalArgumentException("Illegal hexadecimal string: missing suffix \"" + suffix + "\" at index " + i);
                }
                i += suffixLen;
            }
        }
        
        return dataLen;
    }
    
    private static NumberFormatException throwInputException(String input, int index, int start, int end) {
        String msg = input.substring(start, end);
        throw new NumberFormatException("For input string: \"" + msg + "\" at index: " + index);
    }
    
    /**
     * 16进制字符串转byte数组
     *
     * @param payload   16进制字符串
     * @param separator 分隔符
     * @param prefix    前缀
     * @param suffix    后缀
     * @param unitLen   int
     * @return byte[]
     */
    private static byte[] toBytes(String payload, String separator, String prefix, String suffix, int unitLen) {
        if (unitLen < 2) {
            unitLen = 2;
        }
        if(payload == null || payload.length() == 0){
            return new byte[0];
        }
        if(payload.length() == 1){
            payload = "0" + payload;
        }
        
        // 常量定义 分隔符
        final int limit = -Integer.MAX_VALUE, radix = 16, multmin = limit / radix;
        final int len = payload.length();
        final int sepLen = separator == null || separator.isEmpty() ? 0 : separator.length();
        final int prefixLen = prefix == null || prefix.isEmpty() ? 0 : prefix.length();
        final int suffixLen = suffix == null || suffix.isEmpty() ? 0 : suffix.length();
        
        // 检查规则
        int dataLen = getBytesLen(payload, sepLen, separator, prefixLen, prefix, unitLen, radix, suffixLen, suffix);
        
        // 解析数据
        byte[] data = new byte[dataLen];
        int offset = 0;
        for (int i = 0; i < len; ) {
            
            // 分隔符
            if (offset != 0 && sepLen > 0) {
                i += sepLen;
                if (i >= len) {break;}
            }
            
            // 前缀
            if (prefixLen > 0) {
                i += prefixLen;
            }
            
            
            int result = 0;
            boolean negative = false;
            int digit;
            for (int j = 0; j < unitLen; j++) {
                digit = Character.digit(payload.charAt(i++), radix);
                
                if (result < multmin) {
                    throw throwInputException(payload, i, i - j, i - j + unitLen);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw throwInputException(payload, i, i - j, i - j + unitLen);
                }
                
                result -= digit;
            }
            data[offset++] = (byte) (negative ? result : -result);
            // 后缀
            if (suffixLen > 0) {i += suffixLen;}
        }
        
        return data;
    }
    
    
    /**
     * 16进制字符串转byte数组
     *
     * @param payload 16进制字符串
     * @param sep     分隔符
     * @param prefix  前缀
     * @param suffix  后缀
     * @return byte[]
     */
    public static byte[] toBytes(String payload, String sep, String prefix, String suffix) {
        return toBytes(payload, sep, prefix, suffix, 2);
    }
    
    /**
     * 16进制字符串转byte数组
     *
     * @param payload 16进制字符串
     * @param sep     分隔符
     * @param prefix  前缀
     * @return byte[]
     */
    public static byte[] toBytes(String payload, String sep, String prefix) {
        return toBytes(payload, sep, prefix, EMPTY);
    }
    
    /**
     * 16进制字符串转byte数组
     *
     * @param payload 16进制字符串
     * @param sep     分隔符
     * @return byte[]
     */
    public static byte[] toBytes(String payload, String sep) {
        return toBytes(payload, sep, EMPTY);
    }
    
    /**
     * 16进制字符串转byte数组
     *
     * @param payload 16进制字符串
     * @return byte[]
     */
    public static byte[] toBytes(String payload) {
        return toBytes(payload, EMPTY);
    }
    
    
    /**********************************************************************/
    /***************************  Hex String 转 String  *******************/
    /**********************************************************************/
    
    
    /**
     * 16进制字符串，转字符串<br>
     * 例如：<br>
     * hex = 0x65sf--0x66sf--0x88sf<br>
     * 0x：表示前缀
     * sf：表示后缀
     * --：表示分隔符
     *
     * @param payload 16进制字符串
     * @param charset 字符串编码
     * @param sep     16进制字符串中的单位分隔符，比如：65--50--66   其中-- 属于分割符。默认为空
     * @param prefix  16进制字符串中的单位前缀符，比如：0x650x660x88 其中0x 属于前缀，默认为空
     * @param suffix  16进制字符串中的单位后缀符，比如：65xx66xx88xx 其中xx 属于后缀。默认为空
     * @return 转换后的字符串
     */
    public static String fromHex(String payload, Charset charset, String sep, String prefix, String suffix) {
        byte[] bytes = toBytes(payload, sep, prefix, suffix);
        return charset == null ? new String(bytes) : new String(bytes, charset);
    }
    
    
    /**
     * 16进制字符串，转字符串<br>
     * 例如：<br>
     * hex = 0x65--0x66--0x88<br>
     * 0x：表示前缀
     * --：表示分隔符
     *
     * @param payload 16进制字符串
     * @param charset 字符串编码
     * @param sep     16进制字符串中的单位分隔符，比如：65--50--66   其中-- 属于分割符。默认为空
     * @param prefix  16进制字符串中的单位前缀符，比如：0x650x660x88 其中0x 属于前缀，默认为空
     * @return 转换后的字符串
     */
    public static String fromHex(String payload, Charset charset, String sep, String prefix) {
        return fromHex(payload, charset, sep, prefix, EMPTY);
    }
    
    /**
     * 16进制字符串，转字符串<br>
     * 例如：<br>
     * hex = 65--66--88<br>
     * --：表示分隔符
     *
     * @param payload 16进制字符串
     * @param charset 字符串编码
     * @param sep     16进制字符串中的单位分隔符，比如：65--50--66   其中-- 属于分割符。默认为空
     * @return 转换后的字符串
     */
    public static String fromHex(String payload, Charset charset, String sep) {
        return fromHex(payload, charset, sep, EMPTY, EMPTY);
    }
    
    /**
     * 16进制字符串，转字符串<br>
     *
     * @param payload 16进制字符串
     * @param charset 字符串编码
     * @return 转换后的字符串
     */
    public static String fromHex(String payload, Charset charset) {
        return fromHex(payload, charset, EMPTY, EMPTY, EMPTY);
    }
    
    
    /**
     * 16进制字符串，转字符串<br>
     *
     * @param payload 16进制字符串
     * @param sep     16进制字符串中的单位分隔符，比如：65--50--66   其中-- 属于分割符。默认为空
     * @return 转换后的字符串
     */
    public static String fromHex(String payload, String sep) {
        return fromHex(payload, null, sep, EMPTY, EMPTY);
    }
    
    /**
     * 16进制字符串，转字符串
     *
     * @param payload 16进制字符串
     * @return 转换后的字符串
     */
    public static String fromHex(String payload) {
        return fromHex(payload, null, EMPTY, EMPTY, EMPTY);
    }
    
    
    /**
     * 16进制字符串，转字符串
     *
     * @param payload 16进制字符串
     * @return 转换后的UTF8编码的字符串
     */
    public static String fromHexUTF8(String payload) {
        return fromHex(payload, UTF_8_C, EMPTY, EMPTY, EMPTY);
    }
}
